{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.7/site-packages/pandas_datareader/compat/__init__.py:7: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.\n",
      "  from pandas.util.testing import assert_frame_equal\n"
     ]
    }
   ],
   "source": [
    "from pandas_datareader import data as datareader\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from datetime import timedelta, datetime\n",
    "import math\n",
    "import random\n",
    "from itertools import combinations\n",
    "import scipy.stats as ss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.ensemble import BaggingClassifier\n",
    "from sklearn.metrics import confusion_matrix, classification_report, accuracy_score\n",
    "from sklearn.metrics import r2_score, matthews_corrcoef, f1_score, mean_absolute_error\n",
    "from sklearn.model_selection import BaseCrossValidator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import shap"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from mlfinlab.microstructural_features import get_roll_measure, get_roll_impact\n",
    "from mlfinlab.microstructural_features import get_corwin_schultz_estimator, get_bekker_parkinson_vol\n",
    "from mlfinlab.microstructural_features import get_bar_based_kyle_lambda, get_bar_based_amihud_lambda, get_bar_based_hasbrouck_lambda\n",
    "from ta import add_all_ta_features\n",
    "from ta.utils import dropna\n",
    "from mlfinlab.features.fracdiff import frac_diff_ffd\n",
    "from utils import get_meta_barier, getDailyVol"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from mlfinlab.backtest_statistics import sharpe_ratio\n",
    "from mlfinlab.backtest_statistics import probabilistic_sharpe_ratio\n",
    "from mlfinlab.backtest_statistics import deflated_sharpe_ratio\n",
    "from mlfinlab.backtest_statistics import information_ratio\n",
    "from mlfinlab.backtest_statistics import minimum_track_record_length\n",
    "from mlfinlab.backtest_statistics import drawdown_and_time_under_water"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from yahoofinancials import YahooFinancials"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "np.random.seed(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Modeling and Training Routines"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_model():\n",
    "    clf=RandomForestClassifier(n_estimators=1,\n",
    "                               criterion='entropy',\n",
    "                               bootstrap=False,\n",
    "                               class_weight='balanced_subsample')\n",
    "    clf=BaggingClassifier(base_estimator=clf,\n",
    "                         n_estimators=100,\n",
    "                         max_features=1.)\n",
    "    return clf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_and_evaluate_n_times(X_train, Y_train, X_test, Y_test):\n",
    "    mmcs, pred_prs = [], []\n",
    "    for n in range(N_SAMPLES):\n",
    "\n",
    "        clf = create_model()\n",
    "\n",
    "        clf.fit(X_train, Y_train)\n",
    "        pred = clf.predict(X_test)\n",
    "        pred_pr = clf.predict_proba(X_test)[:, 1]\n",
    "\n",
    "        mmcs.append(matthews_corrcoef(Y_test, pred))\n",
    "        pred_prs.append(pred_pr)\n",
    "    return mmcs, pred_prs\n",
    "\n",
    "def visualize_mmcs(mmcs):\n",
    "    plt.figure()\n",
    "    plt.title(str(np.mean(mmcs)))\n",
    "    plt.hist(mmcs)\n",
    "    plt.axvline(np.mean(mmcs), color = 'red')\n",
    "    plt.show()\n",
    "    \n",
    "def backtest_predictions(pred_prs, P_test):\n",
    "    bagging_strategies, bagging_returns = [], []\n",
    "    for pred_pr in pred_prs:\n",
    "        signal = [-(1-p) if p <= 0.5 else p for p in pred_pr]\n",
    "        bagging_strategies.append((signal * P_test).cumsum())\n",
    "        bagging_returns.append(signal * P_test)\n",
    "    return bagging_strategies, bagging_returns\n",
    "\n",
    "def visualize_backtests(bagging_strategies, P_test):\n",
    "    plt.figure(figsize = (15, 5))\n",
    "    for strategy in bagging_strategies:\n",
    "        plt.plot(strategy, color = 'grey', ls = '--', lw=0.5)\n",
    "    plt.plot(P_test.cumsum(), lw = 3, label = 'Benchmark')\n",
    "    plt.plot(np.array(bagging_strategies).mean(axis=0), lw = 3, label = 'Strategy')\n",
    "    plt.legend()\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Dataset Routines"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def ohclv_to_features(data):\n",
    "    \n",
    "    try:\n",
    "        frac_diff_series = frac_diff_ffd(pd.DataFrame(np.log(data['close'])), 0.7, thresh=1e-4)\n",
    "    except:\n",
    "        print('Not calculated')\n",
    "        frac_diff_series = pd.DataFrame(np.log(data['close'])).pct_change()\n",
    "        \n",
    "    # Add all ta features\n",
    "    technical_features = add_all_ta_features(\n",
    "        data, open=\"open\", high=\"high\", low=\"low\", close=\"close\", volume=\"volume\")\n",
    "    \n",
    "    data['feat_tech_volume_cmf'] = technical_features['volume_cmf']\n",
    "    data['feat_tech_volatility_dcl'] = technical_features['volatility_dcl']\n",
    "    data['feat_tech_trend_macd_diff'] = technical_features['trend_macd_diff']\n",
    "    data['feat_tech_trend_vortex_ind_diff'] = technical_features['trend_vortex_ind_diff']\n",
    "    data['feat_tech_momentum_stoch_signal'] = technical_features['momentum_stoch_signal']\n",
    "    data['feat_tech_momentum_tsi'] = technical_features['momentum_tsi']\n",
    "    \n",
    "    data['feat_afml_roll_measure'] = get_roll_measure(data['close'], WINDOW)\n",
    "    data['feat_afml_roll_impact'] = get_roll_impact(data['close'], data['volume'], WINDOW)\n",
    "    data['feat_afml_corwin_schultz'] = get_corwin_schultz_estimator(data['high'], data['low'], WINDOW)\n",
    "    data['feat_afml_bekker_parkinson_vol'] = get_bekker_parkinson_vol(data['high'], data['low'], WINDOW)\n",
    "    data['feat_afml_kyle_lambda'] = get_bar_based_kyle_lambda(data['close'], data['volume'], WINDOW)\n",
    "    data['feat_afml_amihud_lambda'] = get_bar_based_amihud_lambda(data['close'], data['volume'], WINDOW)\n",
    "    data['feat_afml_hasbrouck_lambda'] = get_bar_based_hasbrouck_lambda(data['close'], data['volume'], WINDOW)\n",
    "    \n",
    "    data['feat_stat_min_frac_close'] = frac_diff_series.rolling(WINDOW).min()\n",
    "    data['feat_stat_max_frac_close'] = frac_diff_series.rolling(WINDOW).max()\n",
    "    data['feat_stat_mean_frac_close'] = frac_diff_series.rolling(WINDOW).mean()\n",
    "    data['feat_stat_std_frac_close'] = frac_diff_series.rolling(WINDOW).std()\n",
    "    data['feat_stat_skew_frac_close'] = frac_diff_series.rolling(WINDOW).skew()\n",
    "    data['feat_stat_kurt_frac_close'] = frac_diff_series.rolling(WINDOW).kurt()\n",
    "    data['feat_stat_autocorr_frac_close'] = frac_diff_series.rolling(WINDOW).apply(lambda x: x.autocorr(), raw=False)\n",
    "    \n",
    "    FEATURE_COLUMNS = [d for d in data.columns if 'feat_' in d]\n",
    "    dataset = data[FEATURE_COLUMNS]\n",
    "    \n",
    "    dataset_normalized = {}\n",
    "    for feature_column in dataset.columns:\n",
    "        feature_i = dataset[feature_column]\n",
    "        feature_i_norm = (feature_i - feature_i.rolling(WINDOW).mean()) / feature_i.rolling(WINDOW).std()\n",
    "        dataset_normalized[feature_column] = feature_i_norm\n",
    "    dataset_normalized = pd.DataFrame(\n",
    "           dataset_normalized, index = dataset.index\n",
    "    )\n",
    "    \n",
    "    dataset = dataset_normalized\n",
    "    dataset['close'] = data['close']\n",
    "    dataset = dataset.replace([np.inf, -np.inf], np.nan)\n",
    "    dataset = dataset.dropna()\n",
    "    \n",
    "    return dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def createXY(inputs, outputs, training=False): \n",
    "    \n",
    "    def flatten_features_window(x):\n",
    "        mean = x.mean(axis=1)\n",
    "        std = x.std(axis=1)\n",
    "        low = x.min(axis=1)\n",
    "        high = x.max(axis=1)\n",
    "        open_f = x[:, 0]\n",
    "        close_f = x[:, -1]\n",
    "        return close_f\n",
    "    \n",
    "    X, Y, P, T = [], [], [], []\n",
    "    \n",
    "    # FIXED IID ASSUMPTION (up to some point)\n",
    "    if training:\n",
    "        SKIP = WINDOW + HORIZON\n",
    "    else:\n",
    "        SKIP = 1\n",
    "    \n",
    "    for i in range(INPUT_WINDOW, len(inputs)-HORIZON, SKIP):\n",
    "\n",
    "        if INPUT_WINDOW > 1:\n",
    "            window = inputs[i-INPUT_WINDOW:i].values\n",
    "        else:\n",
    "            window = inputs.iloc[i].values\n",
    "        future = (outputs[i+HORIZON] - outputs[i]) / outputs[i]\n",
    "        future_binary = 1.0 if future > 0 else 0.0\n",
    "\n",
    "        X.append(window)\n",
    "        Y.append(future_binary)\n",
    "        P.append(future)\n",
    "        T.append(outputs.index[i+HORIZON])\n",
    "\n",
    "    X, Y, P = np.array(X), np.array(Y), np.array(P)\n",
    "    \n",
    "    if INPUT_WINDOW > 1:\n",
    "        X = flatten_features_window(X)\n",
    "    return X, Y, P, T"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Metrics Routines"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def ar1(x):\n",
    "    return np.corrcoef(x[:-1], x[1:])[0,1]\n",
    "\n",
    "def autocorr_penalty(x):\n",
    "    n = len(x)\n",
    "    p = np.abs(ar1(x))\n",
    "    return np.sqrt(1 + 2*np.sum([((n - i)/n)*p**i for i in range(1,n)]))\n",
    "\n",
    "def smart_sharpe(x):\n",
    "    return (np.mean(x)/(np.std(x, ddof=1) * autocorr_penalty(x)) * np.sqrt(252))\n",
    "\n",
    "def calculate_all_metrics(benchmark_returns, strategy_returns, dates_array):\n",
    "    \n",
    "    res = {}\n",
    "    \n",
    "    benchmark_sharpe = sharpe_ratio(benchmark_returns, entries_per_year=252)\n",
    "    benchmark_sharpe_smart = smart_sharpe(benchmark_returns)\n",
    "    strategy_sharpe = sharpe_ratio(strategy_returns, entries_per_year=252)\n",
    "    strategy_sharpe_smart = smart_sharpe(strategy_returns)\n",
    "    psr = probabilistic_sharpe_ratio(strategy_sharpe, benchmark_sharpe, len(benchmark_returns))\n",
    "    all_strategy_sharpes = []\n",
    "    for r in bagging_returns:\n",
    "        sr_i = sharpe_ratio(r, entries_per_year=252)\n",
    "        all_strategy_sharpes.append(sr_i)\n",
    "    dsr = deflated_sharpe_ratio(strategy_sharpe, all_strategy_sharpes, len(benchmark_returns))\n",
    "    ir = information_ratio(strategy_returns, benchmark=np.mean(benchmark_returns), entries_per_year=252)\n",
    "    mtrl = minimum_track_record_length(strategy_sharpe, benchmark_sharpe)\n",
    "\n",
    "    res['benchmark_mean_returns'] = np.mean(benchmark_returns)\n",
    "    res['strategy_mean_returns'] = np.mean(strategy_returns) \n",
    "    res['benchmark_sharpe'] = benchmark_sharpe\n",
    "    res['benchmark_sharpe_smart'] = benchmark_sharpe_smart\n",
    "    res['strategy_sharpe'] = strategy_sharpe\n",
    "    res['strategy_sharpe_smart'] = strategy_sharpe_smart\n",
    "    res['probabilistic_sharpe_ratio'] = psr\n",
    "    res['deflated_sharpe_ratio'] = dsr\n",
    "    res['information_ratio'] = ir\n",
    "    res['minimum_track_record_length'] = mtrl\n",
    "\n",
    "    return res\n",
    "\n",
    "def calculate_important_features(dataset_train, cutoff_up = 0, cutoff_down = 3, visualize = False):\n",
    "\n",
    "    X_train, Y_train, P_train, T_train = createXY(dataset_train[FEATURE_COLUMNS], dataset_train['close'], training=True)\n",
    "    X_train_df = pd.DataFrame(X_train, columns = FEATURE_COLUMNS)\n",
    "    clf = RandomForestClassifier(n_estimators=100, class_weight='balanced_subsample', criterion='entropy')\n",
    "    clf.fit(X_train_df, Y_train)\n",
    "    explainer = shap.TreeExplainer(clf)\n",
    "    shap_values = explainer.shap_values(X_train_df)\n",
    "    fi0 = np.abs(shap_values[0]).mean(axis=0)\n",
    "    fi1 = np.abs(shap_values[1]).mean(axis=0)\n",
    "    fi = fi0 + fi1\n",
    "    imp = pd.DataFrame({\n",
    "        'feature': X_train_df.columns.tolist(),\n",
    "        'mean': fi\n",
    "    })\n",
    "    imp = imp.set_index('feature')\n",
    "    \n",
    "    if visualize:\n",
    "        imp.sort_values('mean').plot.barh()\n",
    "        \n",
    "    return imp, imp.sort_values('mean')[::-1][cutoff_up:-cutoff_down].index.values.tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_all_metrics(benchmark_returns, strategy_returns, dates_array, mmcs, pred_prs):\n",
    "    \n",
    "    res = {}\n",
    "    strategy_returns_mean = strategy_returns.mean(axis=0)\n",
    "    \n",
    "    benchmark_sharpe = sharpe_ratio(benchmark_returns, entries_per_year=252)\n",
    "    benchmark_sharpe_smart = smart_sharpe(benchmark_returns)\n",
    "    strategy_sharpe = sharpe_ratio(strategy_returns_mean, entries_per_year=252)\n",
    "    strategy_sharpe_smart = smart_sharpe(strategy_returns_mean)\n",
    "    psr = probabilistic_sharpe_ratio(strategy_sharpe, benchmark_sharpe, len(benchmark_returns))\n",
    "    all_strategy_sharpes = []\n",
    "    for r in strategy_returns:\n",
    "        sr_i = sharpe_ratio(r, entries_per_year=252)\n",
    "        all_strategy_sharpes.append(sr_i)\n",
    "    dsr = deflated_sharpe_ratio(strategy_sharpe, all_strategy_sharpes, len(benchmark_returns))\n",
    "    ir = information_ratio(strategy_returns_mean, benchmark=np.mean(benchmark_returns), entries_per_year=252)\n",
    "    mtrl = minimum_track_record_length(strategy_sharpe, benchmark_sharpe)\n",
    "    \n",
    "    df_for_ddn = pd.DataFrame.from_dict({'Date': dates_array, \n",
    "                                         'Benchmark': benchmark_returns,\n",
    "                                         'Strategy': strategy_returns_mean})\n",
    "    df_for_ddn = df_for_ddn.set_index('Date')\n",
    "    df_for_ddn['Cumulative_Benchmark'] = df_for_ddn.Benchmark.cumsum().round(2)\n",
    "    df_for_ddn['Cumulative_Strategy'] = df_for_ddn.Strategy.cumsum().round(2)\n",
    "    df_for_ddn['HighValue_Benchmark'] = df_for_ddn['Cumulative_Benchmark'].cummax()\n",
    "    df_for_ddn['HighValue_Strategy'] = df_for_ddn['Cumulative_Strategy'].cummax()\n",
    "    df_for_ddn['Drawdown_Benchmark'] = df_for_ddn['Cumulative_Benchmark'] - df_for_ddn['HighValue_Benchmark']\n",
    "    df_for_ddn['Drawdown_Strategy'] = df_for_ddn['Cumulative_Strategy'] - df_for_ddn['HighValue_Strategy']\n",
    "\n",
    "    res['benchmark_mean_returns'] = np.mean(benchmark_returns)\n",
    "    res['strategy_mean_returns'] = np.mean(strategy_returns) \n",
    "    res['benchmark_sharpe'] = benchmark_sharpe\n",
    "    res['benchmark_sharpe_smart'] = benchmark_sharpe_smart\n",
    "    res['strategy_sharpe'] = strategy_sharpe\n",
    "    res['strategy_sharpe_smart'] = strategy_sharpe_smart\n",
    "    res['probabilistic_sharpe_ratio'] = psr\n",
    "    res['deflated_sharpe_ratio'] = dsr\n",
    "    res['information_ratio'] = ir\n",
    "    res['minimum_track_record_length'] = mtrl\n",
    "    res['benchmark_drawdown'] = df_for_ddn['Drawdown_Benchmark'].quantile(.05)\n",
    "    res['strategy_drawdown'] = df_for_ddn['Drawdown_Strategy'].quantile(.05)\n",
    "    res['mmc_mean'] = np.mean(mmcs)\n",
    "    res['mmc_std'] = np.std(mmcs)\n",
    "    res['mmc_sharpe'] = res['mmc_mean'] / res['mmc_std']\n",
    "    res['model_certainty'] = abs(np.array(np.mean(pred_prs)) - 0.5) / 0.5\n",
    "\n",
    "    return res"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Data reading"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "TICKER = 'DB'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "aapl_yf = yahoo_financials_tech = YahooFinancials([TICKER])\n",
    "data = aapl_yf.get_historical_price_data('2000-01-01', '2020-08-01', 'daily')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "data = pd.DataFrame(data[TICKER]['prices'])\n",
    "data = data.set_index('formatted_date')\n",
    "data_original = data.dropna()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Data Preparation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "WINDOW = 14\n",
    "INPUT_WINDOW = 1 # REDUCED THE CONTEXT\n",
    "HORIZON = 1\n",
    "N_SAMPLES = 25"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset = ohclv_to_features(data_original)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "FEATURE_COLUMNS = [d for d in dataset.columns if 'feat_' in d]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CPCV Generation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CPCV(BaseCrossValidator):\n",
    "    # TODO: add purge \"holes\" !!!\n",
    "    def __init__(self, X, N, k):\n",
    "        self.X = X\n",
    "        self.N = N\n",
    "        self.k = k\n",
    "\n",
    "    def generate_eras(self):\n",
    "        # assuming exact division, we will cut-off small piece of time series\n",
    "        # in the very beginning\n",
    "        return np.array(sum([\n",
    "                    [i] * (len(self.X) // self.N) for i in range(self.N)\n",
    "                    ], []\n",
    "                   )\n",
    "        )\n",
    "        \n",
    "    def split(self, X=None, y=None, groups=None):\n",
    "        # removing first m items from time series\n",
    "        eras = self.generate_eras()\n",
    "        len_diff = abs(len(self.X) - len(eras))\n",
    "        comb = list(combinations(range(self.N), self.N-self.k))\n",
    "        all_splits = range(self.N)\n",
    "\n",
    "        for combination in comb:\n",
    "            train_indices, test_indices = [], []\n",
    "            for c in combination:\n",
    "                indices_train = list(np.where(eras == c)[0])\n",
    "                train_indices.extend(indices_train)\n",
    "            for t in list(set(all_splits) - set(combination)):\n",
    "                indices_test = list(np.where(eras == t)[0])\n",
    "                test_indices.extend(indices_test)\n",
    "            yield(train_indices, test_indices)  \n",
    "              \n",
    "    def get_n_splits(self):\n",
    "        comb = combinations(range(self.N), self.N-self.k)\n",
    "        return len(list(comb))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Probability of strategy overfit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "processing split 1 / 35\n",
      "processing split 2 / 35\n",
      "processing split 3 / 35\n",
      "processing split 4 / 35\n",
      "processing split 5 / 35\n",
      "processing split 6 / 35\n",
      "processing split 7 / 35\n",
      "processing split 8 / 35\n",
      "processing split 9 / 35\n",
      "processing split 10 / 35\n",
      "processing split 11 / 35\n",
      "processing split 12 / 35\n",
      "processing split 13 / 35\n",
      "processing split 14 / 35\n",
      "processing split 15 / 35\n",
      "processing split 16 / 35\n",
      "processing split 17 / 35\n",
      "processing split 18 / 35\n",
      "processing split 19 / 35\n",
      "processing split 20 / 35\n",
      "processing split 21 / 35\n",
      "processing split 22 / 35\n",
      "processing split 23 / 35\n",
      "processing split 24 / 35\n",
      "processing split 25 / 35\n",
      "processing split 26 / 35\n",
      "processing split 27 / 35\n",
      "processing split 28 / 35\n",
      "processing split 29 / 35\n",
      "processing split 30 / 35\n",
      "processing split 31 / 35\n",
      "processing split 32 / 35\n",
      "processing split 33 / 35\n",
      "processing split 34 / 35\n",
      "processing split 35 / 35\n",
      "CPU times: user 18min 52s, sys: 12.5 s, total: 19min 5s\n",
      "Wall time: 21min 53s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "N = 7\n",
    "k = 3\n",
    "\n",
    "cpcv = CPCV(dataset, N, k)\n",
    "\n",
    "ALL_P_TESTS_OOS = []\n",
    "ALL_RETURNS_OOS = []\n",
    "ALL_DATES_OOS = []\n",
    "ALL_MMCS_OOS, ALL_PREDS_OOS = [], []\n",
    "FEATURE_IMPORTANCES_OOS = []\n",
    "\n",
    "ALL_P_TESTS_IS = []\n",
    "ALL_RETURNS_IS = []\n",
    "ALL_DATES_IS = []\n",
    "ALL_MMCS_IS, ALL_PREDS_IS = [], []\n",
    "FEATURE_IMPORTANCES_IS = []\n",
    "\n",
    "for e, (train_ids, test_ids) in enumerate(cpcv.split()):\n",
    "    print('processing split', e+1, '/', cpcv.get_n_splits())\n",
    "    \n",
    "    dataset_train, dataset_test = dataset.iloc[train_ids], dataset.iloc[test_ids]\n",
    "    imp, IMPORTANT_FEATURES = calculate_important_features(dataset_train)\n",
    "    \n",
    "    X_train, Y_train, P_train, T_train = createXY(dataset_train[FEATURE_COLUMNS], dataset_train['close'], training=True)\n",
    "    X_test, Y_test, P_test, T_test = createXY(dataset_test[FEATURE_COLUMNS], dataset_test['close'])\n",
    "    \n",
    "    mmcs, pred_prs = train_and_evaluate_n_times(X_train, Y_train, X_test, Y_test)\n",
    "    bagging_strategies, bagging_returns = backtest_predictions(pred_prs, P_test)\n",
    "    \n",
    "    ALL_RETURNS_OOS.append(np.array(bagging_returns))\n",
    "    ALL_P_TESTS_OOS.append(P_test)\n",
    "    ALL_DATES_OOS.append(T_test)\n",
    "    ALL_MMCS_OOS.append(mmcs)\n",
    "    ALL_PREDS_OOS.append(pred_prs)\n",
    "    FEATURE_IMPORTANCES_OOS.append(imp)\n",
    "    \n",
    "    mmcs_is, pred_prs_is = train_and_evaluate_n_times(X_train, Y_train, X_train, Y_train)\n",
    "    bagging_strategies_is, bagging_returns_is = backtest_predictions(pred_prs_is, P_train) \n",
    "    \n",
    "    ALL_RETURNS_IS.append(np.array(bagging_returns_is))\n",
    "    ALL_P_TESTS_IS.append(P_train)\n",
    "    ALL_DATES_IS.append(T_train)\n",
    "    ALL_MMCS_IS.append(mmcs_is)\n",
    "    ALL_PREDS_IS.append(pred_prs_is)\n",
    "    FEATURE_IMPORTANCES_IS.append(imp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_results_is = []\n",
    "for b_ret, s_ret, dates, mmcs, preds, fis in zip(ALL_P_TESTS_IS, \n",
    "                                                 ALL_RETURNS_IS, \n",
    "                                                 ALL_DATES_IS, \n",
    "                                                 ALL_MMCS_IS,\n",
    "                                                 ALL_PREDS_IS,\n",
    "                                                 FEATURE_IMPORTANCES_IS):\n",
    "    res = calculate_all_metrics(\n",
    "            np.array(b_ret), \n",
    "            np.array(s_ret), \n",
    "            [datetime.strptime(date, \"%Y-%m-%d\").date() for date in dates],\n",
    "            mmcs, \n",
    "            fis\n",
    "    )\n",
    "    df_results_is.append(res)\n",
    "df_results_is = pd.DataFrame(df_results_is)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_results_oos = []\n",
    "for b_ret, s_ret, dates, mmcs, preds, fis in zip(ALL_P_TESTS_OOS, \n",
    "                                                 ALL_RETURNS_OOS, \n",
    "                                                 ALL_DATES_OOS, \n",
    "                                                 ALL_MMCS_OOS, \n",
    "                                                 ALL_PREDS_OOS, \n",
    "                                                 FEATURE_IMPORTANCES_OOS):\n",
    "    res = calculate_all_metrics(\n",
    "            np.array(b_ret), \n",
    "            np.array(s_ret), \n",
    "            [datetime.strptime(date, \"%Y-%m-%d\").date() for date in dates],\n",
    "            mmcs, \n",
    "            fis\n",
    "    )\n",
    "    df_results_oos.append(res)\n",
    "df_results_oos = pd.DataFrame(df_results_oos)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "w_c_is = df_results_is['strategy_sharpe']\n",
    "w_c_oos = df_results_oos['strategy_sharpe']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Regression"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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uvnmm3XTTTdp7969+vOf/6zKykrNm1fzC+P999/Xd77zHS1ZskS7d+9WYWGhHn/8ca1du1aS9IMf/EBz5sxRSUmJSktLNXPmzHqvceryqdu3b9eqVavUq1evs6795MmTZ30MgKBG8F577TWlpqZqypQpkqTk5GQtXrxYTz31lD755BM99thjysvL09ChQyVJF1xwgRYuXKif/exnkmpWy0tPT48dLysrq95rNLV86rx58zR48GANHz48tljTyy+/rCuuuEKXX365rrrqqtjz8+fP1+TJkzVixAhNnjxZBQUFuvHGGzV69GhdeumlsdX2JOmZZ55RTk5ObCGn6upqVVdXKy8vT4MGDVJWVpYWL17ckt2JdoigRvB27typYcOGnfZcz549lZGRobfffrvB/bXLkErSnDlzNGbMGI0bN06LFy/WRx99VO81zrR86vHjxzV8+HBt3bpVo0aN0hNPPCFJGjlypN544w299dZbmjRpkhYuXBj7mV27dukPf/iDnnuuZsHJzZs364UXXtC2bdu0YsUKFRUVqbS0VMuXL9cf//hHlZSUKDk5WcuWLVNJSYkOHjyoHTt2aPv27bFfUOi8CGp0eFOmTFFpaakmTpyoTZs2afjw4bG1QmqdafnUrl27avz48ZKkYcOGxT4tpqysTNdee62ysrK0aNGi2C8GSZowYYK6desW27766qvVp08fdevWTTfffLMKCwu1YcMGFRcX6+tf/7qGDBmiDRs2aN++fbrkkku0b98+zZw5U+vWrVPPnj0T3EMIHUGN4A0cOFDFxcWnPXf06FH99a9/1Ze//OUG9xcXF+uyyy6LbX/xi1/UXXfdpRdffFFdunTRjh076r1O7fKpixYt0gMPPKDVq1dLklJSUmKf+p2cnBwbd545c6ZmzJih7du36/HHH48tiyrVXxq17qeGm5ncXXfeeadKSkpUUlKiPXv2aP78+erdu7e2bt2q0aNHa8mSJbr77rub22XoYAhqBC83N1effPKJfvvb30qSqqurNXfuXOXl5encc8/V9OnTVVBQEFsLuqKiQj/60Y903333SZLWrVunqqoqSTVvPFZUVOjiiy8+7TXiWT61ro8//jh2nKeffvqMbV999VUdOXJEf/vb37R69WqNGDFCubm5WrlypQ4fPixJOnLkiN599119+OGHikQiuuWWW7RgwQJt2bKl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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "plt.hist(w_c_is.values, label = 'IS Sharpes', alpha = 0.55)\n",
    "plt.hist(w_c_oos.values, label = 'OOS Sharpes', alpha = 0.55)\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [],
   "source": [
    "import statsmodels.api as sm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [],
   "source": [
    "Y = w_c_oos\n",
    "X = w_c_is\n",
    "X = sm.add_constant(X)\n",
    "model = sm.OLS(Y,X)\n",
    "results = model.fit()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table class=\"simpletable\">\n",
       "<caption>OLS Regression Results</caption>\n",
       "<tr>\n",
       "  <th>Dep. Variable:</th>     <td>strategy_sharpe</td> <th>  R-squared:         </th> <td>   0.021</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Model:</th>                   <td>OLS</td>       <th>  Adj. R-squared:    </th> <td>  -0.009</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Method:</th>             <td>Least Squares</td>  <th>  F-statistic:       </th> <td>  0.7108</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Date:</th>             <td>Sun, 29 Nov 2020</td> <th>  Prob (F-statistic):</th>  <td> 0.405</td> \n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Time:</th>                 <td>20:19:20</td>     <th>  Log-Likelihood:    </th> <td> -4.2922</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>No. Observations:</th>      <td>    35</td>      <th>  AIC:               </th> <td>   12.58</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Df Residuals:</th>          <td>    33</td>      <th>  BIC:               </th> <td>   15.70</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Df Model:</th>              <td>     1</td>      <th>                     </th>     <td> </td>   \n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Covariance Type:</th>      <td>nonrobust</td>    <th>                     </th>     <td> </td>   \n",
       "</tr>\n",
       "</table>\n",
       "<table class=\"simpletable\">\n",
       "<tr>\n",
       "         <td></td>            <th>coef</th>     <th>std err</th>      <th>t</th>      <th>P>|t|</th>  <th>[0.025</th>    <th>0.975]</th>  \n",
       "</tr>\n",
       "<tr>\n",
       "  <th>const</th>           <td>    0.3459</td> <td>    0.428</td> <td>    0.809</td> <td> 0.424</td> <td>   -0.524</td> <td>    1.216</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>strategy_sharpe</th> <td>   -0.0236</td> <td>    0.028</td> <td>   -0.843</td> <td> 0.405</td> <td>   -0.080</td> <td>    0.033</td>\n",
       "</tr>\n",
       "</table>\n",
       "<table class=\"simpletable\">\n",
       "<tr>\n",
       "  <th>Omnibus:</th>       <td> 0.349</td> <th>  Durbin-Watson:     </th> <td>   1.318</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Prob(Omnibus):</th> <td> 0.840</td> <th>  Jarque-Bera (JB):  </th> <td>   0.496</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Skew:</th>          <td> 0.191</td> <th>  Prob(JB):          </th> <td>   0.780</td>\n",
       "</tr>\n",
       "<tr>\n",
       "  <th>Kurtosis:</th>      <td> 2.560</td> <th>  Cond. No.          </th> <td>    138.</td>\n",
       "</tr>\n",
       "</table><br/><br/>Warnings:<br/>[1] Standard Errors assume that the covariance matrix of the errors is correctly specified."
      ],
      "text/plain": [
       "<class 'statsmodels.iolib.summary.Summary'>\n",
       "\"\"\"\n",
       "                            OLS Regression Results                            \n",
       "==============================================================================\n",
       "Dep. Variable:        strategy_sharpe   R-squared:                       0.021\n",
       "Model:                            OLS   Adj. R-squared:                 -0.009\n",
       "Method:                 Least Squares   F-statistic:                    0.7108\n",
       "Date:                Sun, 29 Nov 2020   Prob (F-statistic):              0.405\n",
       "Time:                        20:19:20   Log-Likelihood:                -4.2922\n",
       "No. Observations:                  35   AIC:                             12.58\n",
       "Df Residuals:                      33   BIC:                             15.70\n",
       "Df Model:                           1                                         \n",
       "Covariance Type:            nonrobust                                         \n",
       "===================================================================================\n",
       "                      coef    std err          t      P>|t|      [0.025      0.975]\n",
       "-----------------------------------------------------------------------------------\n",
       "const               0.3459      0.428      0.809      0.424      -0.524       1.216\n",
       "strategy_sharpe    -0.0236      0.028     -0.843      0.405      -0.080       0.033\n",
       "==============================================================================\n",
       "Omnibus:                        0.349   Durbin-Watson:                   1.318\n",
       "Prob(Omnibus):                  0.840   Jarque-Bera (JB):                0.496\n",
       "Skew:                           0.191   Prob(JB):                        0.780\n",
       "Kurtosis:                       2.560   Cond. No.                         138.\n",
       "==============================================================================\n",
       "\n",
       "Warnings:\n",
       "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n",
       "\"\"\""
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = np.arange(np.min(w_c_is), np.max(w_c_is), 0.5)\n",
    "y = 0.3459 + -0.0236 * x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYoAAAEGCAYAAAB7DNKzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nO3deXhU5fXA8e8hLGHfpRBQkCWyJCSQUJFVBKGCQkHqghXcxbpXLNSqqFhosaJUlPorCq4oFIHihrIUUVQSFoHILmoCshPABAnJ+f1xZyaTZDKZrDNJzud55snMvXfmnpkEzrzLPa+oKsYYY0x+qgQ7AGOMMaHNEoUxxhi/LFEYY4zxyxKFMcYYvyxRGGOM8atqsAMoaU2aNNHWrVsHOwxjjClXEhMTj6hqU1/7KlyiaN26NQkJCcEOwxhjyhUR+T6/fUHtehKRISKyQ0R2i8jEfI75nYgkicg2EXmrrGM0xpjKLmgtChEJA2YBg4BkYL2ILFXVJK9j2gOTgF6qelxEzgtOtMYYU3kFs0XRA9itqntV9SwwHxie65jbgFmqehxAVQ+VcYzGGFPpBXOMIgL40etxMvDrXMd0ABCRz4EwYLKqfpT7hUTkduB2gPPPP79UgjV5ZWRkkJyczJkzZ4IdijEmQOHh4bRs2ZJq1aoF/JxQH8yuCrQH+gMtgTUiEqWqJ7wPUtWXgZcB4uLirHhVGUlOTqZu3bq0bt0aEQl2OMaYAqgqR48eJTk5mTZt2gT8vGAmihSgldfjlq5t3pKBr1Q1A/hORHbiJI71ZRNiaFq8MYXpH+9g/4l0WjSoyYTBkYyIjSjzOM6cOWNJwphyRERo3Lgxhw8fLtTzgjlGsR5oLyJtRKQ6cC2wNNcxi3FaE4hIE5yuqL1lGWSoWbwxhUmLtpByIh0FUk6kM2nRFhZvzJ1jy4YlCWPKl6L8mw1aolDVc8DdwMfAt8C7qrpNRJ4Ukatch30MHBWRJGAVMEFVjwYn4tAw/eMdpGdk5tiWnpHJ9I93BCkiY0xFF9TrKFT1A1XtoKptVfVp17bHVHWp676q6oOq2klVo1R1fjDjDQX7T6QXantFFxYWRkxMDF26dOHKK6/kxIkTBT+pjDz22GN8+umnxX6duXPncvfddxfqOX/961+Lfd7i2rdvH126dAEgLS2NMWPGEBUVRZcuXejduzenT58u8XO2bt2aI0eO5NmemprKjTfeSLt27Wjbti033ngjqampnv3btm1jwIABREZG0r59e5566inca/UcPHiQYcOG0bVrVzp16sQVV1zh89xPP/00nTt3Jjo6mpiYGL766iu/MZUnVuupnGnRoGahtld0NWvWZNOmTWzdupVGjRoxa9asYr/muXPnSiAyePLJJxk4cGCJvFZhhUKi8Pb888/TrFkztmzZwtatW5kzZ06hZt0U1y233MKFF17I7t272bNnD23atOHWW28FID09nauuuoqJEyeyY8cONm/ezBdffMGLL74IOAl/0KBBbN68maSkJKZNm5bn9detW8eyZcvYsGED33zzDZ9++imtWrXKc1xhldTfYnFZoihnJgyOpGa1sBzbalYLY8LgyCBFFDp69uxJSoozVrNnzx6GDBlC9+7d6dOnD9u3b/dsv/jii4mKiuIvf/kLderUAWD16tX06dOHq666ik6dOpGZmcmECROIj48nOjqaf/3rXwAcOHCAvn37eloxn332GZmZmYwbN44uXboQFRXFjBkzABg3bhwLFy4EYMWKFcTGxhIVFcXNN9/ML7/8AjjfNh9//HG6detGVFSUJ87cfvzxR/r370/79u154oknPNvfeOMNevToQUxMDHfccQeZmZlMnDiR9PR0YmJiGDNmDNOnT2fmzJkAPPDAAwwYMACAlStXMmbMGACWL19Oz5496RoTy5ArR/DljmS2HzjJqs+/pF+/fnTv3p3Bgwdz4MABAPr378+f/vQnevToQYcOHfjss8/8/m4OHDhARET2hIvIyEhq1KiR57jx48cTFxdH586defzxxz3b8/ucjh49yuWXX07nzp259dZb8bVi5+7du0lMTOTRRx/1bHvsscdISEhgz549vPXWW/Tq1YvLL78cgFq1avHCCy94EsKBAwdo2bKl57nR0dE+31+TJk0876lJkya0aNHCs/+f//xnnti//vprevbsSWxsLJdccgk7djjdx3PnzuWqq65iwIABXHbZZaxevZq+ffsydOhQIiMjufPOO8nKygKyf2/dunVj9OjRnlbaxIkT6dSpE9HR0Tz00EP5/2ICpaoV6ta9e3et6N7bkKyXTF2hrf+0TC+ZukLf25AclDiSkpJybujXL+9t1ixn388/+97/6qvO/sOH8+4LQO3atVVV9dy5c3r11Vfrhx9+qKqqAwYM0J07d6qq6pdffqmXXnqpqqoOHTpU33rrLVVVfemllzzPX7VqldaqVUv37t2rqqr/+te/9KmnnlJV1TNnzmj37t117969+swzz+iUKVM85zx58qQmJCTowIEDPTEdP35cVVXHjh2rCxYs0PT0dG3ZsqXu2LFDVVV///vf64wZM1RV9YILLtCZM2eqquqsWbP0lltuyfMeX331Vf3Vr36lR44c0bS0NO3cubOuX79ek5KSdNiwYXr27FlVVR0/frzOmzcvx+eiqrpu3Tq9+uqrVVW1d+/eGh8fr2fPntXJkyfr7Nmz9fDhw9qnTx/98dAx3ZJ8Qu+b9LiO/+MkTdh7SLt276E79zl/X/Pnz9ebbrpJVVX79eunDz74oKqqvv/++3rZZZflifu7777Tzp07q6rqxo0btWnTpnrxxRfrI4884vnd5Hb06FHPZ9uvXz/dvHmz38/pnnvu0SeeeEJVVZctW6aAHj58OMdrLlmyREeMGJHnXCNGjNAlS5boAw88oM8991ye/Q0aNNDU1FT96KOPtH79+tq/f3+dMmWKpqSk5Dn21KlT2rVrV23fvr2OHz9eV69e7dmXX+ypqamakZGhqqqffPKJjhw5UlWd33dERITns1i1apXWqFFD9+zZo+fOndOBAwfqggULPL+306dPq6rqtGnT9IknntAjR45ohw4dNCsrS1Wz/x695fm3q6pAgubz/2qoX0dhfBgRGxGU6bChyP3NOSUlhY4dOzJo0CBOnz7NF198wejRoz3Hub/Br1u3jsWLFwNw/fXX5/i21aNHD8/c8uXLl/PNN994WgSpqans2rWL+Ph4br75ZjIyMhgxYgQxMTFceOGF7N27l3vuuYehQ4d6vpm67dixgzZt2tChQwcAxo4dy6xZs7j//vsBGDlyJADdu3dn0aJFPt/noEGDaNy4sef4tWvXUrVqVRITE4mPj/d8Fuedl7fKTffu3UlMTOTkyZPUqFGDbt26kZCQwGeffcbMmTP58ssvSUpKYlD/fihKRkYG0d3i2bdnF7t3fMtVV/yGGtWqkJmZSfPmzT2v6x33vn37/P6eYmJi2Lt3L8uXL+fTTz8lPj6edevW0bFjxxzHvfvuu7z88sucO3eOAwcOkJSU5PkG7+tzWrNmjef+0KFDadiwod84imLw4MHs3buXjz76iA8//JDY2Fi2bt1K06bZhVbr1KlDYmIin332GatWreKaa65h2rRpjBs3Lt/YU1NTGTt2LLt27UJEyMjI8LzeoEGDaNSokedxjx49uPDCCwG47rrrWLt2LeHh4SQlJdGrVy8Azp49S8+ePalfvz7h4eHccsstDBs2jGHDhhX7M7BEYUrO6tX576tVy//+Jk3878+He4wiLS2NwYMHM2vWLMaNG0eDBg3YtGlToV6rdu3anvuqyj//+U8GDx6c57g1a9bw/vvvM27cOB588EFuvPFGNm/ezMcff8zs2bN59913eeWVVwI+r7u7IiwsLN8+6dxTGkUEVWXs2LFMnTrV7+tXq1aNNm3aMHfuXC655BKio6NZtWoVu3fvpmPHjuzZs4dBgwYxafpLOZ6369tttO1wEa8vWU50ywZFittbnTp1GDlyJCNHjqRKlSp88MEHORLFd999xzPPPMP69etp2LAh48aNy3HVf2HP59apUyc2bdpEVlYWVao4ve1ZWVls2rSJTp06cejQIdasWZPjOXv37qVOnTrUq1cPgEaNGnH99ddz/fXXM2zYMNasWcOoUaNyPCcsLIz+/fvTv39/oqKimDdvnidR+Ir90Ucf5dJLL+W9995j37599O/f3/Na3n+LkP/vf9CgQbz99tt53vPXX3/NihUrWLhwIS+88AIrV64M+PPyxcYoTIVQq1YtZs6cyT/+8Q9q1apFmzZtWLBgAeD8p79582YALr74Yv7zn/8AMH9+/pPoBg8ezEsvveT5lrdz505+/vlnvv/+e5o1a8Ztt93GrbfeyoYNGzhy5AhZWVmMGjWKKVOmsGHDhhyvFRkZyb59+9i9ezcAr7/+Ov369SvU+/vkk084duwY6enpLF68mF69enHZZZexcOFCDh1ySqAdO3aM7793KkVXq1YtxzfUPn368Mwzz9C3b1/69OnD7NmziY2NRUS4+OKL+fzzzznww3cApKX9zL69u2ndtj3Hjx5h20anbH9GRgbbtm0rVNxun3/+OcePHwecb75JSUlccMEFOY45efIktWvXpn79+hw8eJAPP/ywwNft27cvb73lFJX+8MMPPefw1q5dO2JjY5kyZYpn25QpU+jWrRvt2rVjzJgxrF271jNDLT09nXvvvZeHH34YcMZy0tLSADh16hR79uzJUypox44d7Nq1y/N406ZNed5fbqmpqZ5x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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "plt.scatter(w_c_is, w_c_oos, label = 'Backtests IS vs OOS Sharpes')\n",
    "plt.plot(x, y, color = 'red', ls = '--', label = 'Regression between IS and OOS Sharpes')\n",
    "plt.xlabel('Sharpe ratios IS')\n",
    "plt.ylabel('Sharpe ratios OOS')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Logits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_star = np.argmax(w_c_is)\n",
    "w_c = (ss.rankdata(w_c_oos) - n_star) / len(w_c_oos)\n",
    "w_c = w_c + np.abs(np.min(w_c))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_c = np.log(w_c / (1 - w_c))\n",
    "y_c[y_c==-np.inf] = 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.6507251193096577"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_c_neg = y_c[y_c < 0]\n",
    "y_c_neg = (y_c_neg - y_c_neg.min()) / (y_c_neg.max() - y_c_neg.min())\n",
    "pbo = np.mean(y_c_neg)\n",
    "pbo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "plt.hist(y_c, label = 'Logits $w_c$, PBO = ' + str(np.round(pbo, 2)))\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Strategy risk"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [],
   "source": [
    "def binHR(sl, pt, n, tSR):\n",
    "    a = (n + tSR**2) * (pt - sl)**2\n",
    "    b = (2*n*sl - tSR**2*(pt-sl))*(pt-sl)\n",
    "    c = n*sl**2\n",
    "    p = (-b + (b**2 - 4*a*c)**0.5) / (2.*a)\n",
    "    return p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [],
   "source": [
    "def probFailure(ret, freq, tSR):\n",
    "    # Derive probability that strategy may fail\n",
    "    rPos, rNeg = ret[ret>0].mean(), ret[ret<=0].mean()\n",
    "    p = ret[ret>0].shape[0] / float(ret.shape[0])\n",
    "    thresP = binHR(rNeg, rPos, freq, tSR)\n",
    "    risk = ss.norm.cdf(thresP, p, p*(1-p)) # approximation to bootstrap\n",
    "    return risk"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 136,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "failure_probs = []\n",
    "for returns in ALL_RETURNS_OOS:\n",
    "    some_returns = returns.mean(axis=0)\n",
    "    pf = probFailure(some_returns, 260, 0.0)\n",
    "    failure_probs.append(pf)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 137,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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DBgwgJycHF566aXoulatjpw98tDUs4eMHDmShQsXRttPnjyZfv368e677/LMM8+Qn59Pbm4uP/jBD6ioqDhie9nZ2dx6661HTJU7fvx4brjhBs455xxuvfVWCgsLOffcc+nbty+jR4+O3oADqqYNyM3NpU+fPrVOyjVs2DA2bdpEbm4uixcvrnNbNaf3rW7Lli1kZWVFn/ft2/ew7+GYMWM488wzueqqqzh08eRdd93FwIED6dOnDxMnToy+XnM/h/qal5fH6aefHj3ar6io4Kc//SkDBw6kb9++/O53vzuib0uXLqVnz5706NGDFi1acPnllx/2Mzyk+vTIY8aMYf78+STiIk8VdAmdPZ7BHs8IOkZc9u/fH52Cd/To0WRkZPCnP/2JlStXsmDBAiZPntzgf+j79u3jnHPOYdWqVdE/69955x0KCwtJTU2tdTpg+OdUuZMmTYpOnwtVsysuWbKE++67j6uvvpq7776b1atXk5OTw5133hltV1JSQmFhIf/7v//Ltddee8T2586dy9e//nUKCwu58MILj7qtQ9P7Tp48+bBt3HjjjVx33XUMHjyYX//612zevDm67q9//Su//e1v+fDDD9mwYQPvvFM1lDZp0iSWLVvGBx98wP79+w8blqm5n6KiIpYuXcqrr77KDTfcQGlpKU888QStW7dm2bJlLFu2jMcee4zPPvvssFybNm2ia9eu0edZWVls2rTpiO9B9XZpaWm0bt06egeqxtCQi4RO15SquxBtrDz2L9+vOeRSVlbGz3/+cxYtWkRKSgqbNm1i69atdOzYsd7bTk1N5bLLLgNg/vz5rFixIjpJ1v79+4+YwfGQ6lPlVh/fHjt2LKmpqezevZtdu3ZFJ+8aN24cY8eOPeL9F110EXv27GHXrl20aVP7zyLWtuq6td7w4cPZsGEDb7zxBq+//jr9+/ePfqaQn58fPXrPzc2lqKiIQYMGsWDBAu655x5KSkrYuXMnvXv35tvf/nat+/nOd75DSkoKvXr1okePHnz88ce8+eabrF69OjrR2e7du1m7di3du3evNWMQVNAldHqnfQ7AxoPHfkGv6dlnn2X79u2sWLGC9PR0srOzKS0trbN9WloalZWV0efV22ZkZETHzd2dcePG8d///d8xM9Q2VS40bKrd2p7Xx9H22bZtW6688kquvPJKRo4cyaJFi2jX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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure()\n",
    "plt.hist(failure_probs, label = 'Failure prob for Sharpe 0.0')\n",
    "plt.axvline(0.5, ls = '--', color = 'grey', label = '50% probability')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "35"
      ]
     },
     "execution_count": 120,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(ALL_RETURNS_OOS)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
